// Copyright (C) 2023 即时通讯网(52im.net) & Jack Jiang.
// The RainbowChat Project. All rights reserved.
// 
// 【本产品为著作权产品，合法授权后请放心使用，禁止外传！】
// 【本次授权给：<北京小羊驼科技有限公司>，授权编号：<NT231212144350>，代码指纹：<A.702363430.550>，技术对接人微信：<ID: wxid_wbyootvkdcgj22>】
// 【授权寄送：<收件：苏先生、地址：北京市大兴区北京密码西区6号楼1单元301、电话：18613885610、邮箱：bd@huiyuanxiang-inc.com>】
// 
// 【本系列产品在国家版权局的著作权登记信息如下】：
// 1）国家版权局登记名(简称)和权证号：RainbowChat    （证书号：软著登字第1220494号、登记号：2016SR041877）
// 2）国家版权局登记名(简称)和权证号：RainbowChat-Web（证书号：软著登字第3743440号、登记号：2019SR0322683）
// 3）国家版权局登记名(简称)和权证号：RainbowAV      （证书号：软著登字第2262004号、登记号：2017SR676720）
// 4）国家版权局登记名(简称)和权证号：MobileIMSDK-Web（证书号：软著登字第2262073号、登记号：2017SR676789）
// 5）国家版权局登记名(简称)和权证号：MobileIMSDK    （证书号：软著登字第1220581号、登记号：2016SR041964）
// * 著作权所有人：江顺/苏州网际时代信息科技有限公司
// 
// 【违法或违规使用投诉和举报方式】：
// 联系邮件：jack.jiang@52im.net
// 联系微信：hellojackjiang
// 联系QQ号：413980957
// 授权说明：http://www.52im.net/thread-1115-1-1.html
// 官方社区：http://www.52im.net
package com.x52im.rbchat.network.im;

import android.app.Activity;
import android.content.Context;
import android.util.Log;

import com.eva.android.AppManager;
import com.eva.android.widget.WidgetUtils;
import com.eva.android.widget.WidgetUtils.ToastType;
import com.eva.android.widget.alert.AlertDialog;
import com.x52im.rbchat.IMClientManager;
import com.x52im.rbchat.MyApplication;
import com.x52im.rbchat.R;
import com.x52im.rbchat.network.http.async.QueryOfflineChatMsgAsync;
import com.x52im.rbchat.network.http.async.QueryOfflineFriendsReqAsync;
import com.x52im.rbchat.utils.PreferencesToolkits;
import com.x52im.rbchat.utils.ToolKits;

import net.x52im.mobileimsdk.android.ClientCoreSDK;
import net.x52im.mobileimsdk.android.event.ChatBaseEvent;
import net.x52im.mobileimsdk.server.protocal.s.PKickoutInfo;

import java.text.MessageFormat;
import java.util.Observer;

/**
 * 与IM服务器的底层连接事件在此ChatBaseEvent子类中实现即可。
 * <p>
 *     本类是MobileIMSDK的基础通信消息的回调事件接口实现类（将接收如：登陆成功事件 通知、掉线事件通知等）。
 * </p>
 * <p>
 *     RaincowChat的IM通信底层是基于MobileIMSDK即时通讯框架实现，
 *     如需了解RainbowChat的通信层原理，详情请前往了解MobileIMSDK框架，
 *     地址是：http://www.52im.net/thread-52-1-1.html
 * </p>
 *
 * @author Jack Jiang
 * @since 1.0
 */
public class ChatBaseEventListener implements ChatBaseEvent
{
	private final static String TAG = ChatBaseEventListener.class.getSimpleName();
	
	private Context context;

    /** 本Observer目前仅用于登陆时（因为登陆与收到服务端的登陆验证结果是异步的，所以有此观察者来完成收到验证后的处理）.*/
	private Observer loginOkForLaunchObserver = null;
	
	/** 
	 * 与IM服务器的网络连接状态观察者.
	 * @see net.x52im.mobileimsdk.android.ClientCoreSDK#isConnectedToServer() */
	private Observer networkStatusObserver = null;
	
	public ChatBaseEventListener(Context context)
	{
		this.context = context;
	}

	/**
	 * 与MobileIMSDK框架服务端的通信断开的回调事件通知。
	 * <p>
	 * 		该消息只有在客户端连接服务器成功之后网络异常中断之时触发。 导致与与服务端的通信断开的原
	 * 		因有（但不限于）：无线网络信号不稳定、WiFi与2G/3G/4G等同开情 况下的网络切换、手机系统的省电策略等。
	 * </p>
	 *
	 * @param errorCode 本回调参数表示表示连接断开的原因，目前错误码没有太多意义，仅作保留字段，目前通常为-1
	 */
	@Override
	public void onLinkClose(int errorCode) {
		Log.e(TAG, "服务器连接已断开，error：" + errorCode);
		
		// 通知网络连接状态观察者
		if(networkStatusObserver != null)
			networkStatusObserver.update(null, null);
		
		// @see FriendsListProvider.offlineAll()的注释说明
		MyApplication.getInstance2().getIMClientManager().getFriendsListProvider().offlineAll();
	}

	/**
	 * 本地用户的MobileIMSDK框架登陆/重连结果回调事件通知。
	 *
	 * @param errorCode 服务端反馈的登录结果：0 表示登陆/重连成功，否则为服务端自定义的出错代码（按照约定通常为>=1025的数）
	 */
	@Override
	public void onLoginResponse(int errorCode) {
		// 登陆成功或掉线重连成功
		if (errorCode == 0) {
            Log.i(TAG, "连接IM服务器成功！服务端响应码="+errorCode);

			// 此观察者只有开启程序首次使用登陆界面时有用
			if(loginOkForLaunchObserver != null) {
				loginOkForLaunchObserver.update(null, errorCode);
				loginOkForLaunchObserver = null;
			}

			// 登陆/重连成功后要做的事
			afterLinkSucess();
		} else {
			WidgetUtils.showToast(context, MessageFormat.format(context.getResources().getString(R.string.login_failure_by_code), errorCode), ToastType.ERROR);
			Log.e(TAG, "与IM服务器连接失败，错误代码：" + errorCode);

			// token检验失败
			if(errorCode == 1025) {
				Log.e(TAG, "与IM服务器连接失败，原因是token校验失败，马上提示用户并告之重新登录！");

				// 释放长连接相关的资源
				ClientCoreSDK.getInstance().release();
				// 并显示提示信息
				ToolKits.showAlertAndGotoLogin(context
						, context.getString(R.string.token_invalid_title)
						, context.getString(R.string.token_invalid_content_for_socket));
			}
		}
		
		// 通知网络连接状态观察者：连接状态已改变
		if(networkStatusObserver != null)
			networkStatusObserver.update(null, null);
	}

	/**
	 * 本的用户被服务端踢出的回调事件通知。
	 *
	 * @param kickoutInfo 被踢信息对象，{@link PKickoutInfo} 对象中的 code字段定义了被踢原因代码
	 */
	@Override
	public void onKickout(PKickoutInfo kickoutInfo) {
		Log.e(TAG, "【DEBUG_UI】已收到服务端的\"被踢\"指令，kickoutInfo.code：" + kickoutInfo.getCode());

		String alertContent = "";
		if(kickoutInfo.getCode() == PKickoutInfo.KICKOUT_FOR_DUPLICATE_LOGIN){
			alertContent = context.getString(R.string.kickout_prompt_comtent_duplicate_login);
		} else if(kickoutInfo.getCode() == PKickoutInfo.KICKOUT_FOR_ADMIN){
			alertContent = context.getString(R.string.kickout_prompt_comtent_for_admin);
		} else{
			alertContent = MessageFormat.format(context.getString(R.string.kickout_prompt_comtent_others), kickoutInfo.getReason());
		}

		// 显示提示信息
		ToolKits.showAlertAndGotoLogin(context, context.getString(R.string.kickout_prompt_title), alertContent);
	}

	/**
	 * 登陆/重连成功后要做的事。
	 */
	private void afterLinkSucess() {
		final IMClientManager imc = MyApplication.getInstance2().getIMClientManager();

		//** 优先载入首页"消息"界面中的历史数据、预定义数据等（自230824起，本加载已从AlarmsFragment中的_initExtraDatas
		//   方法移至此处，确保在离线消息加载完成前执行，否则极端情况下会出现首页消息重复item的情况）。
		//   补充：本方法是线程安全的，且在整个APP生命周期中，数据会保证只加载1次，所以可放心调用，无需担心多次加载。
		imc.getAlarmsProvider().loadDatasOnceAsync();

		//** 登陆/重连成功后就刷新好友列表（随时保证好友列表与服务端保持一致）
		imc.getFriendsListProvider().refreshRosterAsync(context);

		//** 登陆/重连成功后就刷新"我"的群聊列表（随时保证群聊列表与服务端保持一致）
		imc.getGroupsProvider().refreshGroupsListAsync(context);

		//** 登陆/重连成功后，尝试获取本人可能收到的离线消息（更新）
		// 【本代码用途有2个】：
		//  1）首次启运APP连接服务器成功后拉取离线消息；
		//  2）因网络不好掉线重连后的再次接取离线消息（因为掉线后的这个时间内，很可能会有人发送消的啦）。
		new QueryOfflineChatMsgAsync(context).execute();

		//** 获取未读加好友请求数（包括好友发请求时我不在线的情况），并尝试在首页“消息”里放入一条验证消息的表格item
		new QueryOfflineFriendsReqAsync(context).execute();
	}

	public void setLoginOkForLaunchObserver(Observer loginOkForLaunchObserver) {
		this.loginOkForLaunchObserver = loginOkForLaunchObserver;
	}

	public void setNetworkStatusObserver(Observer networkStatusObserver) {
		this.networkStatusObserver = networkStatusObserver;
	}
}